/*
 * Copyright (c) 2022-2023 Termony Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "los_context.h"
#include "securec.h"
#include "los_arch_context.h"
#include "los_arch_interrupt.h"
#include "los_task.h"
#include "los_sched.h"
#include "los_interrupt.h"
#include "los_debug.h"

#define PORT_SP_EL0 ((UINT64) 0x00)


/* FPU上下文标识, 在任务切换时如果需要保存置1. */
volatile UINTPTR g_taskHasFPUContext = 0;
/* 任务调度请求标识, 在中断中请求调度任务时置1. */
volatile UINTPTR g_taskYieldRequired = 0;


/**
 * @brief  处理器底层初始化.
 * @note   该接口在LiteOS初始化时被调用.
 */
LITE_OS_SEC_TEXT_INIT VOID ArchInit(VOID)
{
    HalHwiInit();
}

/**
 * @brief  任务退出函数.
 * @note   该接口在任务退出时被调用.
 */
LITE_OS_SEC_TEXT_MINOR NORETURN VOID ArchSysExit(VOID)
{
    (VOID)LOS_IntLock();
    while (1) {
    }
}

/**
 * @brief  任务栈初始化.
 * @param  taskID 任务ID.
 * @param  stackSize 任务栈大小.
 * @param  topStack 指向任务栈栈顶的指针.
 * @note   该接口在任务创建时被调用.
 */
LITE_OS_SEC_TEXT_INIT VOID *ArchTskStackInit(UINT32 taskID, UINT32 stackSize, VOID *topStack)
{
    TaskContext *context = (TaskContext *)((UINTPTR)topStack + stackSize - sizeof(TaskContext));
    UINTPTR currentel;

    __asm__ __volatile__("MRS %0, CurrentEL" : "=r"(currentel) :: "memory");

    context->x1 = 0x0101010101010101ULL;
    context->x0 = (UINTPTR) taskID;
    context->x3 = 0x0303030303030303ULL;
    context->x2 = 0x0202020202020202ULL;
    context->x5 = 0x0505050505050505ULL;
    context->x4 = 0x0404040404040404ULL;
    context->x7 = 0x0707070707070707ULL;
    context->x6 = 0x0606060606060606ULL;
    context->x9 = 0x0909090909090909ULL;
    context->x8 = 0x0808080808080808ULL;
    context->x11 = 0x1111111111111111ULL;
    context->x10 = 0x1010101010101010ULL;
    context->x13 = 0x1313131313131313ULL;
    context->x12 = 0x1212121212121212ULL;
    context->x15 = 0x1515151515151515ULL;
    context->x14 = 0x1414141414141414ULL;
    context->x17 = 0x1717171717171717ULL;
    context->x16 = 0x1616161616161616ULL;
    context->x19 = 0x1919191919191919ULL;
    context->x18 = 0x1818181818181818ULL;
    context->x21 = 0x2121212121212121ULL;
    context->x20 = 0x2020202020202020ULL;
    context->x23 = 0x2323232323232323ULL;
    context->x22 = 0x2222222222222222ULL;
    context->x25 = 0x2525252525252525ULL;
    context->x24 = 0x2424242424242424ULL;
    context->x27 = 0x2727272727272727ULL;
    context->x26 = 0x2626262626262626ULL;
    context->x29 = 0x2929292929292929ULL;
    context->x28 = 0x2828282828282828ULL;
    context->sp = (UINTPTR)topStack + stackSize;
    context->x30 = (UINTPTR)ArchSysExit;
    context->spsr = currentel | PORT_SP_EL0;
    context->elr = (UINTPTR)OsTaskEntry;

#if (LOSCFG_PLATFORM_FPU_ENABLE > 0)
    memset_s(context->v, sizeof(context->v), 0, sizeof(context->v));
    context->fpsr = 0x0000000000000000ULL;
    context->fpcr = 0x0000000000000000ULL;
#endif /* LOSCFG_PLATFORM_FPU_ENABLE */

    return (VOID *)context;
}

#if (LOSCFG_KERNEL_SIGNAL == 1)
VOID *ArchSignalContextInit(VOID *stackPointer, VOID *stackTop, UINTPTR sigHandler, UINT32 param)
{
    UNUSED(stackTop);
    TaskContext *context = (TaskContext *)((UINTPTR)stackPointer - sizeof(TaskContext));
    UINTPTR currentel;

    __asm__ __volatile__("MRS %0, CurrentEL" : "=r"(currentel) :: "memory");
    (VOID)memset_s((VOID *)context, sizeof(TaskContext), 0, sizeof(TaskContext));

    context->spsr = currentel | PORT_SP_EL0;
    context->elr = (UINTPTR)sigHandler;
    context->x0 = (UINTPTR) param;

    return (VOID *)context;
}
#endif

/**
 * @brief  任务调度请求.
 * @note   该接口在需要任务调度时被调用.
 */
LITE_OS_SEC_TEXT_INIT VOID ArchTaskSchedule(VOID)
{
    if (g_intCount > 0) {
        g_taskYieldRequired = 1;
    } else {
        UINTPTR currentel;
        __asm__ __volatile__("MRS %0, CurrentEL" : "=r"(currentel) :: "memory");
        if (currentel == 0xc) {
            __asm__ __volatile__("SMC 0");
        } else if (currentel == 0x8) {
            __asm__ __volatile__("HVC 0");
        } else if (currentel == 0x4) {
            __asm__ __volatile__("SVC 0");
        }
    }
}

/**
 * @brief  任务调度启动.
 * @note   该接口在LiteOS启动时被调用.
 */
LITE_OS_SEC_TEXT_INIT UINT32 ArchStartSchedule(VOID)
{
    (VOID)LOS_IntLock();
    OsSchedStart();
    HalStartToRun();
    return LOS_OK; /* never return */
}
